/*
 * Created on 2007-11-23
 *
 * TODO
 */
package jacky.lanlan.song.aop;

import jacky.lanlan.song.aop.advice.AfterAdvice;
import jacky.lanlan.song.aop.advice.AfterReturningAdvice;
import jacky.lanlan.song.aop.advice.AroundAdvice;
import jacky.lanlan.song.aop.advice.MethodBeforeAdvice;
import jacky.lanlan.song.reflection.ReflectionUtils;

import java.lang.reflect.Method;

import org.aopalliance.aop.Advice;

/**
 * Advice处理器。
 * 
 * @author Jacky.Song
 */
interface AdviceProcessor {
	
	/**
	 * 处理 Advice
	 * 
	 * @param otherParam
	 *          处理Advice可能用到的其他参数
	 * @param advice
	 *          要处理的Advice
	 * @param obj
	 *          被advice的对象
	 * @param pointcut
	 *          连接点(被拦截的方法)
	 * @param args
	 *          方法参数
	 * @return 处理Advice后的返回值
	 */
	Object processAdvice(Object otherParam,Advice advice,Object obj,Method pointcut,Object... args);
	
	/**
	 * 默认 AroundAdvice 处理器，处理 AroundAdvice 类型的拦截。
	 */
	AdviceProcessor AROUND_ADVICE_PROCESSOR = new AdviceProcessor() {
		public Object processAdvice(Object otherParam,Advice advice,Object obj,Method pointcut,Object... args) {
			AroundAdvice around=(AroundAdvice) advice;
			Object returnValue=null;
			try {
				returnValue=around.process(obj, pointcut, args);
			}
			catch (Exception e) {
				throw new RuntimeException(e);
			}
			return returnValue;
		}
	};
	
	/**
	 * 默认 MethodBeforeAdvice 处理器，处理 MethodBeforeAdvice 类型的拦截。
	 */
	AdviceProcessor BEFORE_ADVICE_PROCESSOR = new AdviceProcessor() {
		public Object processAdvice(Object otherParam,Advice advice,Object obj,Method pointcut,Object... args) {
			MethodBeforeAdvice before=(MethodBeforeAdvice) advice;
			try {
				before.before(pointcut, args, obj);
			}
			catch (Throwable e) {
				throw new RuntimeException(e);
			}
			return null;
		}
	};
	
	/**
	 * 默认 AfterReturningAdvice 处理器，处理 AfterReturningAdvice 类型的拦截。
	 */
	AdviceProcessor AFTER_RETURN_ADVICE_PROCESSOR = new AdviceProcessor() {
		public Object processAdvice(Object otherParam,Advice advice,Object obj,Method pointcut,Object... args) {
			try {
				// 这里的otherParam是returnValue
				((AfterReturningAdvice)advice).afterReturning(otherParam, pointcut, args, obj);
			}
			catch (Throwable e) {
				throw new RuntimeException(e);
			}
			return null;
		}
	};
	
	/**
	 * 默认 ThrowsAdvice 处理器，处理 ThrowsAdvice 类型的拦截。
	 */
	AdviceProcessor THROWS_ADVICE_PROCESSOR = new AdviceProcessor() {
		public Object processAdvice(Object otherParam,Advice advice,Object obj,Method pointcut,Object... args) {
			Exception e = (Exception) otherParam;// 这里的otherParam是Exception
			Class<?> adviceClass=advice.getClass();
			Method afterThrowingMethod=ReflectionUtils.findMethod(adviceClass, "afterThrowing",
					new Class[] {pointcut.getClass(), args.getClass(), Object.class,Exception.class});
			if(afterThrowingMethod!=null) {
				// 优先调用 void afterThrowing(Method, args, target, ThrowableSubclass) 这种签名类型的方法
				try {
					afterThrowingMethod.invoke(advice,new Object[] {pointcut,args,obj,e});
				}
				catch (Exception e1) {
					throw new RuntimeException(e1.getCause());
				}
				//ReflectionUtils.invokeMethod(afterThrowingMethod, advice, new Object[] {pointcut,args,obj,e});
			}
			else {
				// 如果没有这种方法，调用 void afterThrowing(ThrowableSubclass) 这种类型的方法
				afterThrowingMethod=
					ReflectionUtils.findMethod(adviceClass, "afterThrowing",new Class[] {Exception.class});
				if(afterThrowingMethod!=null) {
					try {
						afterThrowingMethod.invoke(advice,new Object[] {e});
					}
					catch (Exception e1) {
						throw new RuntimeException(e1.getCause());
					}
					ReflectionUtils.invokeMethod(afterThrowingMethod,advice, new Object[] {e});
				}
				else {
					// 如果这两种都没有，抛出异常
					throw new RuntimeException(adviceClass.getSimpleName()+
							"没有符合 \"void afterThrowing([Method, args, target], ThrowableSubclass)\" 类型的方法");
				}
			}
			return null;
		}
	};
	
	/**
	 * 默认 AfterAdvice 处理器，处理 AfterAdvice 类型的拦截。
	 */
	AdviceProcessor AFTER_ADVICE_PROCESSOR = new AdviceProcessor() {
		public Object processAdvice(Object otherParam, Advice advice, Object obj, Method pointcut,Object... args) {
			try {
				((AfterAdvice)advice).finalAction();
			}
			catch (Throwable e) {
				throw new RuntimeException(e);
			}
			return null;
		}
	};

}
